package com.yu8home.asyc.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import com.yu8home.asyc.service.AaService;
import com.yu8home.asyc.service.BbService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncExecutionInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 异步方法和调用者方法在同一个类中，为什么异步方法与调用者在同一个内中会失效？
 * 原因是：spring 在扫描bean的时候会扫描方法上是否包含@Async注解，如果包含，spring会为这个bean动态地生成一个子类（即代理类，proxy），代理类是继承原来那个bean的。
 * 此时，当这个有注解的方法被调用的时候，实际上是由代理类来调用的，代理类在调用时增加异步作用。
 * 然而，如果这个有注解的方法是被同一个类中的其他方法调用的，那么该方法的调用并没有通过代理类，而是直接通过原来的那个bean，所以就没有增加异步作用。
 */
@Service
@Slf4j
public class AaServiceImpl implements AaService {
    @Autowired
    public BbService bbService;

    @Override
    public void a() {
        t();
        log.info("AaServiceImpl.a...");
        m();

        bbService.b();
        log.info("AaServiceImpl.a!!!");
    }

    // self-invocation – calling the async method from within the same class – won’t work
    @Async
    public void m() {
        log.info("AaServiceImpl.m...");
        ThreadUtil.sleep(5 * 1000);
        log.info("AaServiceImpl.m!!!");
    }

    private void t() {
        /**
         * CPU密集型：尽可能地避免线程间上下文切换，一般会设置为N+1，为了防止意外情况出现线程阻塞，如果出现阻塞，多出来的线程会继续执行任务，保证CPU的利用效率
         * IO密集型
         */
        int curSystemThreads = Runtime.getRuntime().availableProcessors() * 2;
        log.info("curSystemThreads={}", curSystemThreads);

        ThreadPoolTaskExecutor a;
        ThreadPoolExecutor b;

        /**
         * 在使用@Async时，如果不指定线程池的名称，@Async有默认线程池，使用的是Spring默认的线程池SimpleAsyncTaskExecutor
         *
         *
         默认线程池的默认配置：
         默认核心线程数：  8
         最大线程数：     Integet.MAX_VALUE
         队列使用：       LinkedBlockingQueue
         容量是：         Integet.MAX_VALUE
         空闲线程保留时间： 60s
         线程池拒绝策略：   AbortPolicy
         *
         *
         * 四种策略：
         ThreadPoolExecutor.AbortPolicy:         丢弃任务，抛出RejectedExecutionException异常。
         ThreadPoolExecutor.DiscardPolicy：      丢弃任务，但不抛出异常。
         ThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试重新执行任务。
         ThreadPoolExecutor.CallerRunsPolicy：   重试添加当前的任务，自动重复调用 execute() 方法，直到成功
         */
        SimpleAsyncTaskExecutor c;

        ScheduledThreadPoolExecutor d;

        Executors.newFixedThreadPool(8);
        Executors.newCachedThreadPool();// 神奇的无存储空间阻塞队列，每个 put 必须要等待一个 take
        Executors.newSingleThreadExecutor();

        AsyncExecutionInterceptor e;
        // AutoMapperConfig f;
    }


    /**
     * 被@Async标注的方法必须是void类型的，且不能有返回值，除非返回类型是Future，这样可以通过Future获取异步操作的结果
     */
    @Async
    @Override
    public CompletableFuture<List<String>> completableFutureTask(String start) {
        log.warn("xxxxx-{}", Thread.currentThread().getName());

        List<String> movies = Arrays.asList("Forrest Gump", "Titanic", "Spirited Away", "The Shawshank Redemption", "Zootopia", "Farewell ", "Joker", "Crawl");
        List<String> results = movies.stream().filter(movie -> movie.startsWith(start)).collect(Collectors.toList());
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture(results);
    }

}
